package com.smartwebx.ha.dal.util.core;

import java.util.ArrayList;
import java.util.Calendar;
import java.util.Collections;
import java.util.Date;
import java.util.List;
import java.util.Map;
import java.util.Map.Entry;
import java.util.Set;

import org.apache.commons.lang3.StringUtils;
import org.apache.commons.lang3.time.DateUtils;

import com.google.common.base.Objects;
import com.google.common.base.Optional;
import com.google.common.base.Predicate;
import com.google.common.base.Splitter;
import com.google.common.collect.Collections2;
import com.google.common.collect.Iterables;
import com.google.common.collect.Lists;
import com.google.common.collect.Ordering;
import com.smartwebx.ha.dal.inter.water.IFeatureFlowDao;
import com.smartwebx.ha.dal.inter.water.IFeatureWaterDao;
import com.smartwebx.ha.dal.inter.water.HDAndBDGCProcessDataOperatDao;
import com.smartwebx.ha.dal.inter.water.IQProcessDataOperatDao;
import com.smartwebx.ha.dal.inter.water.ISLMProcessDataOperatDao;
import com.smartwebx.ha.dal.inter.water.IZProcessDataOperatDao;
import com.smartwebx.ha.dal.usebean.DateValue;
import com.smartwebx.ha.dal.usebean.FeatureHighLow;
import com.smartwebx.ha.dal.usebean.SplitDateAndTimePointBean;
import com.smartwebx.ha.dal.usebean.SplitDatePointBean;
import com.smartwebx.ha.dal.usebean.WaterYearComp;
import com.smartwebx.ha.dal.usebean.abstr.AbstractValue;
import com.smartwebx.ha.dal.util.DateFullProcess;
import com.smartwebx.ha.dal.util.DateValueHandler;
import com.smartwebx.ha.dal.util.SplitDateAndTimePoint;
import com.smartwebx.ha.dal.util.SplitDatePoint;

/**
 * 水情过程数据分析
 * 
 * @author zhangxi
 * 
 */
public class WaterProcessDatasAnalysis {

	/**
	 * 返回没有经过处理的水位（水库、河道、闸坝、堰闸、潮位） 或者 流量（水库、河道、闸坝、堰闸）过程数据
	 * 
	 * @param datasProvide
	 * @param stcd
	 * @param startDate
	 * @param endDate
	 * @param operType
	 * @return
	 */
	public static List<DateValue> getPurenessProcesss(Object datasProvide,
			String stcd, Date startDate, Date endDate, String operType) {
		Date[] startEndPoint = getStartEndPoint(startDate, endDate);
		startDate = startEndPoint[0];
		endDate = startEndPoint[1];

		List<DateValue> result = null;

		if (Objects.equal(operType, "water")) {
			result = ((IZProcessDataOperatDao) datasProvide)
					.getSingleStatHourZProcess(stcd, startDate, endDate);
		} else if (Objects.equal(operType, "flow")) {
			result = ((IQProcessDataOperatDao) datasProvide)
					.getSingleStatHourQProcess(stcd, startDate, endDate);
		} else {
			Iterable<String> strs = Splitter.on('-').split(operType);
			String arg1 = Iterables.get(strs, 1);
			String arg2 = Iterables.get(strs, 2);
			result = ((ISLMProcessDataOperatDao) datasProvide)
					.getPurenessSoilProcesss(stcd, startDate, endDate, arg1,
							arg2);
		}

		return result;
	}

	private static Date[] getStartEndPoint(Date start, Date end) {
		Date now = new Date();
		int nowHour = (int) DateUtils.getFragmentInHours(now, Calendar.DATE);
		now = DateUtils.truncate(now, Calendar.DATE);
		Date newEnd = DateUtils.truncate(end, Calendar.DATE);

		if (Objects.equal(newEnd, now)) {
			end = DateUtils.addHours(end, nowHour + 1);
		} else {
			end = DateUtils.addDays(newEnd, 1);
		}

		return new Date[] { start, end };
	}

	/**
	 * 单站小时水位（水库、河道、闸坝、堰闸、潮位）、流量（水库、河道、闸坝、堰闸）过程 （取每日8时数据）
	 * 
	 * @param datasProvide
	 *            数据提供接口
	 * @param stcd
	 * @param startHour
	 * @param endHour
	 * @param operType
	 * @return
	 */
	public static List<DateValue> getSingleStatHourProcess(Object datasProvide,
			String stcd, Date startHour, Date endHour, String operType) {
		SplitDatePointBean splitDatePoint = SplitDatePoint.splitDatePoint(
				startHour, endHour);
		String operatType = splitDatePoint.getOperatType();
		Date startTime = splitDatePoint.getStartDate();
		Date endTime = splitDatePoint.getEndDate();
		Date nowDate = new Date();
		int nowHour = (int) DateUtils
				.getFragmentInHours(nowDate, Calendar.DATE);
		nowDate = DateUtils.truncate(nowDate, Calendar.DATE);

		List<DateValue> result = null;

		Date bTm = null, eTm = null;
		Date dateFullBTm = null, dateFullETm = null;
		if ("127".indexOf(operatType) > -1) {
			bTm = nowDate;
			eTm = DateUtils.addHours(nowDate, nowHour + 1);

			dateFullBTm = nowDate;
			dateFullETm = DateUtils.addHours(nowDate, nowHour);
		} else if ("45".indexOf(operatType) > -1) {
			bTm = startTime;
			eTm = DateUtils.addHours(nowDate, nowHour + 1);

			dateFullBTm = startTime;
			dateFullETm = DateUtils.addHours(nowDate, nowHour);
		} else if (Objects.equal("6", operatType)) {
			bTm = startTime;
			eTm = DateUtils.addDays(endTime, 1);

			dateFullBTm = startTime;
			dateFullETm = DateUtils.addHours(eTm, -1);
		} else if (Objects.equal("3", operatType)) {
			bTm = startTime;
			eTm = DateUtils.addDays(startTime, 1);

			dateFullBTm = startTime;
			dateFullETm = DateUtils.addHours(eTm, -1);
		}

		DateValue beforeDv = null;

		if (Objects.equal(operType, "water")) {
			result = ((IZProcessDataOperatDao) datasProvide)
					.getSingleStatHourZProcess(stcd, bTm, eTm);
		} else if (Objects.equal(operType, "flow")) {
			result = ((IQProcessDataOperatDao) datasProvide)
					.getSingleStatHourQProcess(stcd, bTm, eTm);
			beforeDv = ((IQProcessDataOperatDao) datasProvide)
					.getStartHourBeforeQVal(stcd, bTm);
		}

		if (!result.isEmpty()) {
			// 根据时间排序
			Collections.sort(result, DateValueHandler.byTmOrder());
			// 获得第一个数据
			DateValue firstDv = result.get(0);
			Date tm = firstDv.getTm();
			long firstHour = DateUtils.getFragmentInHours(tm, Calendar.DATE);

			// 如果开始数据不是 0 时开始，则取上条有效数据放在第一位
			if (firstHour != 0) {
				if (Objects.equal("water", operType)) {
					DateValue beforeData = ((IZProcessDataOperatDao) datasProvide)
							.getStartHourBeforeZVal(stcd, bTm);
					beforeData.setTm(bTm);
					result.add(0, beforeData);
				}
			}

			// 对流量进行处理
			if (Objects.equal(operType, "flow")) {
				if (beforeDv == null) {
					beforeDv = new DateValue(null, 0d);
				}
				for (DateValue dv : result) {
					Double val = dv.getVal();
					if (val != null && val != 0d) {
						beforeDv = dv;
					}
					dv.setVal(beforeDv.getVal());
				}
			}
		}

		Map<Date, Double> timeFullMap = DateFullProcess.returnTimeSectStr(
				dateFullBTm, dateFullETm);

		return pullProcessDataByTime(timeFullMap, result);
	}

	public static List<DateValue> getSingleStatDayProcess(Object datasProvide,
			String stcd, Date startDate, Date endDate, String type) {
		List<DateValue> result = Lists.newArrayList();

		SplitDatePointBean splitDatePoint = SplitDatePoint.splitDatePoint(
				startDate, endDate);
		String operatType = splitDatePoint.getOperatType();
		Date startTime = splitDatePoint.getStartDate();
		Date endTime = splitDatePoint.getEndDate();

		Date nowDate = DateUtils.truncate(new Date(), Calendar.DATE);

		Date bTm = null, eTm = null;
		Date dateFullBTm = null, dateFullETm = null;
		if ("1237".indexOf(operatType) > -1) {
			if (Objects.equal("2", operatType)) {
				DateValue dv = new DateValue();
				dv.setTm(nowDate);
				dv.setVal(0d);
				result.add(dv);
			} else {
				if (startTime != null) {
					bTm = startTime;
				} else {
					bTm = nowDate;
				}
				eTm = DateUtils.addDays(bTm, 1);
				List<DateValue> dvs = getDayProcess(datasProvide, stcd, bTm,
						eTm, type);
				if (dvs == null || dvs.isEmpty()) {
					DateValue dv = new DateValue(bTm, 0d);
					if (dvs == null)
						dvs = Lists.newArrayList();
					dvs.add(dv);
				}
				result = dvs;
			}

		} else if ("45".indexOf(operatType) > -1) {
			bTm = startTime;
			eTm = DateUtils.addDays(endTime, 2);

			List<DateValue> dvs = getDayProcess(datasProvide, stcd, bTm, eTm,
					type);

			dateFullBTm = startTime;
			dateFullETm = DateUtils.addDays(eTm, -1);
			Map<Date, Double> timeFullMap = DateFullProcess.returnDateSectStr(
					dateFullBTm, dateFullETm, 8);
			result = pullProcessDataByTime(timeFullMap, dvs);

		} else if (Objects.equal("6", operatType)) {
			bTm = startTime;
			eTm = DateUtils.addDays(endTime, 1);

			List<DateValue> dvs = getDayProcess(datasProvide, stcd, bTm, eTm,
					type);

			dateFullBTm = startTime;
			Date now = new Date();
			if (now.before(eTm)) {
				long hour = DateUtils.getFragmentInHours(now, Calendar.DATE);
				if (hour < 8) {
					dateFullETm = DateUtils.addDays(now, -1);
				} else {
					dateFullETm = now;
				}
			} else {
				dateFullETm = endTime;
			}
			Map<Date, Double> timeFullMap = DateFullProcess.returnDateSectStr(
					dateFullBTm, dateFullETm, 8);

			result = pullProcessDataByTime(timeFullMap, dvs);
		}

		if (!result.isEmpty()) {
			DateValue firstDv = result.get(0);
			// 如果过程数据以0开头，则认为此信息无效，取开始时间的上一条有效始数据
			if (firstDv.getVal() == 0d) {
				Double beforeVal = 0d;
				if (Objects.equal("water", type)) {
					DateValue obj = ((IZProcessDataOperatDao) datasProvide)
							.getStartHourBeforeZVal(stcd, startTime);
					if (obj != null) {
						beforeVal = obj.getVal();
					} else {
						beforeVal = 0d;
					}
				} else if (StringUtils.startsWith(type, "soil")) {
					Iterable<String> typeIter = Splitter.on('-').split(type);
					String selSLMName = StringUtils.trim(Iterables.get(
							typeIter, 1));
					String exKey = StringUtils.trim(Iterables.get(typeIter, 2));

					if (Objects.equal(selSLMName, "")) {
						beforeVal = ((ISLMProcessDataOperatDao) datasProvide)
								.getStartHourBeforeAvgVal(stcd, startTime,
										exKey).getVal();
					} else {
						beforeVal = ((ISLMProcessDataOperatDao) datasProvide)
								.getStartHourBeforeSelVal(stcd, bTm,
										selSLMName, exKey).getVal();
					}
				}
				// - 2015-01-14 by xx
				// firstDv.setVal(beforeVal);
				// result.add(0, firstDv);
				// + 2015-01-14 by xx
				for (DateValue r : result) {
					if (r.getVal() == 0d) {
						r.setVal(beforeVal);
					} else {
						break;
					}
				}
			}
		}
		return result;
	}

	private static List<DateValue> getDayProcess(Object datasProvide,
			String stcd, Date bTm, Date eTm, String type) {
		List<DateValue> dvs = null;
		if (Objects.equal("water", type)) {
			dvs = ((IZProcessDataOperatDao) datasProvide)
					.getSingleStatDayZProcess(stcd, bTm, eTm);
		} else if (Objects.equal("flow", type)) {
			dvs = ((IQProcessDataOperatDao) datasProvide)
					.getSingleStatDayQProcess(stcd, bTm, eTm);
		} else if (StringUtils.startsWith(type, "soil")) {
			Iterable<String> typeIter = Splitter.on('-').split(type);
			String selSLMName = StringUtils.trim(Iterables.get(typeIter, 1));
			String exKey = StringUtils.trim(Iterables.get(typeIter, 2));

			if (Objects.equal(selSLMName, "")) {
				dvs = ((ISLMProcessDataOperatDao) datasProvide)
						.getSingleStatDayAvgProcess(stcd, bTm, eTm, exKey);
			} else {
				dvs = ((ISLMProcessDataOperatDao) datasProvide)
						.getSingleStatDaySelProcess(stcd, bTm, eTm, selSLMName,
								exKey);
			}
		}

		return dvs;
	}

	public static WaterYearComp getMoreYearCompSort(Object datasProvide,
			String stcd, Iterable<DateValue> maxMinAvgDv, String type) {
		WaterYearComp waterYearComp = new WaterYearComp();

		List<FeatureHighLow> featureHLs = null;

		if (Objects.equal(type, "water")) {
			featureHLs = ((IFeatureWaterDao) datasProvide)
					.getMoreYearStatHighLowWater(stcd);
		} else if (Objects.equal(type, "flow")) {
			featureHLs = ((IFeatureFlowDao) datasProvide)
					.getMoreYearStatHighLowFlow(stcd);
		}

		DateValue maxDv = Iterables.get(maxMinAvgDv, 0);
		DateValue minDv = Iterables.get(maxMinAvgDv, 1);
		DateValue avgDv = Iterables.get(maxMinAvgDv, 2);

		// 年最大值过程
		List<DateValue> maxYearDv = Lists.newArrayList();
		// 年最小值过程
		List<DateValue> minYearDv = Lists.newArrayList();
		// 年平均值过程
		List<DateValue> avgYearDv = Lists.newArrayList();

		maxYearDv.add(maxDv);
		minYearDv.add(minDv);
		avgYearDv.add(avgDv);

		for (FeatureHighLow featureHL : featureHLs) {
			DateValue hisMaxDv = new DateValue(featureHL.getMaxzTm(),
					featureHL.getMaxz());
			maxYearDv.add(hisMaxDv);

			DateValue hisMinDv = new DateValue(featureHL.getMinzTm(),
					featureHL.getMinz());
			minYearDv.add(hisMinDv);

			DateValue hisAvgDv = new DateValue(featureHL.getAvgTm(),
					featureHL.getAvgz());
			avgYearDv.add(hisAvgDv);
		}
		Double hisAvgVal = DateValueHandler.avgByObj(avgYearDv);
		Ordering<AbstractValue> orderByVal = DateValueHandler.byValueOrder();

		Collections.sort(maxYearDv, orderByVal.reverse());
		List<Integer> maxSortHistory = Lists.newArrayList(
				maxYearDv.indexOf(maxDv) + 1, maxYearDv.size());

		Collections.sort(minYearDv, orderByVal);
		List<Integer> minSortHistory = Lists.newArrayList(
				minYearDv.indexOf(minDv) + 1, minYearDv.size());

		Collections.sort(avgYearDv, orderByVal);
		List<Integer> avgSortHistory = Lists.newArrayList(
				avgYearDv.indexOf(avgDv) + 1, avgYearDv.size());

		waterYearComp.setMaxHisArray(maxYearDv);
		waterYearComp.setMaxSotrHistory(maxSortHistory);
		waterYearComp.setMaxProcessPoint(maxDv);

		waterYearComp.setMinHisArray(minYearDv);
		waterYearComp.setMinSotrHistory(minSortHistory);
		waterYearComp.setMinProcessPoint(minDv);

		waterYearComp.setAvgHisArray(avgYearDv);
		waterYearComp.setAvgSotrHistory(avgSortHistory);
		waterYearComp.setAvgValue(avgDv.getVal());

		waterYearComp.setAvgHisValue(hisAvgVal);

		return waterYearComp;
	}

	/**
	 * 填充过程列表
	 * 
	 * @param timeFullMap
	 * @param result
	 * @return
	 */
	private static List<DateValue> pullProcessDataByTime(
			Map<Date, Double> timeFullMap, List<DateValue> result) {
		Set<Entry<Date, Double>> timeFullSet = timeFullMap.entrySet();
		List<DateValue> timeFullDvs = null;

		if (timeFullMap.size() != result.size()) {
			timeFullDvs = Lists.newArrayList();
			for (Entry<Date, Double> timeEntry : timeFullSet) {
				final Date time = timeEntry.getKey();
				Optional<DateValue> findPoint = Iterables.tryFind(result,
						new Predicate<DateValue>() {
							@Override
							public boolean apply(DateValue input) {
								Date inputTm = input.getTm();
								inputTm = DateUtils.truncate(inputTm,
										Calendar.HOUR_OF_DAY);
								return Objects.equal(inputTm, time);
							}
						});
				DateValue dv = new DateValue();
				dv.setTm(time);
				if (findPoint.isPresent()) {
					DateValue findDv = findPoint.get();
					dv.setVal(findDv.getVal());
					timeFullDvs.add(dv);
				} else if (!timeFullDvs.isEmpty()) { // 如果没有查询时间的数据，则使用上条数据填充到当前时间
					dv.setVal(Iterables.getLast(timeFullDvs).getVal());
					timeFullDvs.add(dv);
				} else {
					dv.setVal(0d);
					timeFullDvs.add(dv);
				}
			}
		} else {
			timeFullDvs = result;
		}

		return timeFullDvs;
	}

}
